package org.az.netty_study.p02_nio组件之channel;

import org.az.netty_study.ByteBufferUtil;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * _FileChannel只能工作在阻塞模式下，所以不能和selector配合实现多路复用
 */
public class _1_FileChannel常用方法 {
    public static void main(String[] args) throws IOException {
        /**
         * 虽然channel是双向的，但是如果它的源头是单向，那么channel某种意义上也是单向的
         * FileChannel只能通过FileInputStream、FileOutputStream或者RandomAccessFiLe获取，他们都有getChannel方法
         *  1.通过FileInputStream获取的channel只能读
         *  2.FileOutputStream获取的channel只能写
         *  3.RandomAccessFiLe获取的channel是否能读写，根据RandomAccessFiLe的读写模式来决定
         */
        ByteBuffer buffer = ByteBuffer.allocate(16);
        /**
         * 1.读取
         * channel.read(buffer)：含义是，(buffer)从channel读取数据(数据方向:channel ---> buffer)
         */
        FileInputStream is = new FileInputStream("src\\main\\resources\\data.txt");
        FileChannel channel1 = is.getChannel();
        int read = channel1.read(buffer);
        ByteBufferUtil.debugAll(buffer);

        /**
         * 2.写入
         * channel.read(buffer):含义是，(buffer)向channel写入数据(数据方向:buffer ---> channel)
         * 注意，有些channel(SocketChannel)的写能力有上限，无法将buffer的全部数据一次性写入channel，所以这时需要循环多次写入并判断buffer是否还有剩余数据
         */
        FileOutputStream os = new FileOutputStream("src\\main\\resources\\data2.txt");
        FileChannel channel2 = os.getChannel();
        //写入的正确姿势
        buffer.flip();
        channel2.position(3);
        while (buffer.hasRemaining()) {
            channel2.write(buffer);
        }

        /**
         * 3.关闭
         * channel有close方法、
         * FileInputStream、FileOutputStream或者RandomAccessFiLe的close方法也会间接关闭channel
         */
        channel1.close();

        /**
         * 4.位置position
         * 跟buffer里的position类似，表示channel读到了或者写到了文件的哪个位置
         * 可以设置position:channel2.position(int)，那么读取或写入时就从文件的这个位置开始
         * 如果设置到文件末尾：
         *  1.这时读取为返回-1(相当于读完了嘛)
         *  2.这时写入，会追加内容(因为是从文件末尾开始写).如果position超过了文件末尾，则在新内容和原末尾之间会有空洞(00)
         */
        long position = channel2.position();
        System.out.println(position);

        /**
         * 5.文件大小
         */
        System.out.println(channel2.size());

        /**
         * 6.强制写入
         * channel的write方法并不会立即将数据写入文件，而是会将数据先写入操作系统的缓存里，然后channel关闭时才同步到磁盘
         * 如果要立即写入,可以调用force(true)方法
         */
        channel2.force(true);
    }
}
